package lucene;

import com.alibaba.fastjson.JSON;
import dbutils.DBSql;
import dbutils.DataScore;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;

/**
 * @author Xiongx
 * @version 1.0
 * @date 2021/7/31 13:25
 * @since JDK 1.8
 */
public class LuceneTemp {



    private static String URL="jdbc:mysql://localhost:3306/spring-demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC";

    private static String DRIVER="com.mysql.jdbc.Driver";

    private static String USERNAME="root";

    private static String PASSWORD="admin";

    public static void main(String[] args) throws Exception {
        DBSql dbSql=new DBSql(DataScore.MY_SQL());
        List<Map<String, Object>> mapList=dbSql.selectMapObjList("SELECT * FROM `spring-demo`.`m_note` LIMIT 0,1000");
        //indexSearch1("jdbc");
        createIndex(mapList);
    }


    public static void indexSearch1(String name) throws Exception{
        // 1. 创建分析器对象(Analyzer), 用于分词
        Analyzer analyzer = new StandardAnalyzer();

        // 2. 创建查询对象(Query)
        // 2.1 创建查询解析器对象
        // 参数一:默认的搜索域, 参数二:使用的分析器
        QueryParser queryParser = new QueryParser("title", analyzer);
        // 2.2 使用查询解析器对象, 实例化Query对象
        Query query = queryParser.parse("title:"+name);

        // 3. 创建索引库目录位置对象(Directory), 指定索引库位置
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));

        // 4. 创建索引读取对象(IndexReader), 用于读取索引
        IndexReader indexReader = DirectoryReader.open(directory);

        // 5. 创建索引搜索对象(IndexSearcher), 用于执行索引
        IndexSearcher searcher = new IndexSearcher(indexReader);

        // 6. 使用IndexSearcher对象执行搜索, 返回搜索结果集TopDocs
        // 参数一:使用的查询对象, 参数二:指定要返回的搜索结果排序后的前n个
        TopDocs topDocs = searcher.search(query, 10);

        // 7. 处理结果集
        // 7.1 打印实际查询到的结果数量
        System.out.println("实际查询到的结果数量: " + topDocs.totalHits);
        // 7.2 获取搜索的结果数组
        // ScoreDoc中有文档的id及其评分
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            System.out.println("= = = = = = = = = = = = = = = = = = =");
            // 获取文档的id和评分
            int docId = scoreDoc.doc;
            float score = scoreDoc.score;
            System.out.println("文档id= " + docId + " , 评分= " + score);
            // 根据文档Id, 查询文档数据 -- 相当于关系数据库中根据主键Id查询数据
            Document doc = searcher.doc(docId);
            System.out.println("图书Id: " + doc.get("title"));
        }

        // 8. 关闭资源
        indexReader.close();
    }

    public static void createIndex1(List<Map<String, Object>> mapList) throws IOException {


        // 2. 创建文档对象
        List<Document> documents = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            Document document = new Document();
            // 给文档对象添加域
            // add方法: 把域添加到文档对象中, field参数: 要添加的域
            // TextField: 文本域, 属性name:域的名称, value:域的值, store:指定是否将域值保存到文档中
            document.add(new TextField("title", map.get("title") + "", Field.Store.YES));
            document.add(new TextField("tag", map.get("tag")+"",  Field.Store.YES));

            // 将文档对象添加到文档对象集合中
            documents.add(document);
        }
        // 3. 创建分析器对象(Analyzer), 用于分词
        Analyzer analyzer = new StandardAnalyzer();
        // 4. 创建索引配置对象(IndexWriterConfig), 用于配置Lucene
        // 参数一:当前使用的Lucene版本, 参数二:分析器
        IndexWriterConfig indexConfig = new IndexWriterConfig( analyzer);
        // 5. 创建索引库目录位置对象(Directory), 指定索引库的存储位置
        File path = new File("C:\\Users\\xiong\\Desktop\\index");
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));
        // 6. 创建索引写入对象(IndexWriter), 将文档对象写入索引
        IndexWriter indexWriter = new IndexWriter(directory, indexConfig);
        // 7. 使用IndexWriter对象创建索引
        for (Document doc : documents) {
            // addDocement(doc): 将文档对象写入索引库
            indexWriter.addDocument(doc);
        }
        // 8. 释放资源
        indexWriter.close();
    }

    public void delete(String filed, String keyWord) throws IOException {
        Analyzer analyzer = new StandardAnalyzer();
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));
        IndexWriterConfig iwConfig = new IndexWriterConfig(analyzer);
        // 在原来的索引的基础上创建或新增
        iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        IndexWriter iwriter = new IndexWriter(directory, iwConfig);
        // 删除filed中含有keyWord的索引
        iwriter.deleteDocuments(new Term(filed, keyWord));
        // 提交事务
        iwriter.commit();
        // 关闭事务
        iwriter.close();
    }




    /**
     * 创建索引并进行存储
     *
     * @param title
     * @param content
     * @throws IOException
     */
    public void index(String title, String content) throws IOException {
        Analyzer analyzer = new StandardAnalyzer();
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));
        IndexWriterConfig iwConfig = new IndexWriterConfig(analyzer);
        // 设置创建索引模式(在原来的索引的基础上创建或新增)
        iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        /*
         * 添加索引，在之前的索引基础上追加
         * iwConfig.setOpenMode(OpenMode.APPEND);
         * 创建索引，删除之前的索引
         * iwConfig.setOpenMode(OpenMode.CREATE);
         */
        IndexWriter iwriter = new IndexWriter(directory, iwConfig);
        // 创建一个存储对象
        Document doc = new Document();
        // 添加字段
        doc.add(new TextField("title", title, Field.Store.YES));
        doc.add(new TextField("content", content, Field.Store.YES));
        // 新添加一个doc对象
        iwriter.addDocument(doc);
        // 创建的索引数目
        int numDocs = iwriter.numDocs();
        System.out.println("共索引了: " + numDocs + " 个对象");
        // 提交事务
        iwriter.commit();
        // 关闭事务
        iwriter.close();

    }






    public static void indexSearch(String name) throws Exception{
        // 1. 创建分析器对象(Analyzer), 用于分词
        Analyzer analyzer = new StandardAnalyzer();

        // 2. 创建查询对象(Query)
        // 2.1 创建查询解析器对象
        // 参数一:默认的搜索域, 参数二:使用的分析器
        QueryParser queryParser = new QueryParser("title", analyzer);
        // 2.2 使用查询解析器对象, 实例化Query对象
        Query query = queryParser.parse("title:"+name);

        // 3. 创建索引库目录位置对象(Directory), 指定索引库位置
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));

        // 4. 创建索引读取对象(IndexReader), 用于读取索引
        IndexReader indexReader = DirectoryReader.open(directory);

        // 5. 创建索引搜索对象(IndexSearcher), 用于执行索引
        IndexSearcher searcher = new IndexSearcher(indexReader);

        // 6. 使用IndexSearcher对象执行搜索, 返回搜索结果集TopDocs
        // 参数一:使用的查询对象, 参数二:指定要返回的搜索结果排序后的前n个
        TopDocs topDocs = searcher.search(query, 10);

        // 7. 处理结果集
        // 7.1 打印实际查询到的结果数量
        System.out.println("实际查询到的结果数量: " + topDocs.totalHits);
        // 7.2 获取搜索的结果数组
        // ScoreDoc中有文档的id及其评分
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            System.out.println("= = = = = = = = = = = = = = = = = = =");
            // 获取文档的id和评分
            int docId = scoreDoc.doc;
            float score = scoreDoc.score;
            System.out.println("文档id= " + docId + " , 评分= " + score);
            // 根据文档Id, 查询文档数据 -- 相当于关系数据库中根据主键Id查询数据
            Document doc = searcher.doc(docId);
            System.out.println("图书Id: " + doc.get("title"));
        }

        // 8. 关闭资源
        indexReader.close();
    }

    public String displayHtmlHighlight(Query query, String fieldName, String fieldContent)
            throws IOException, InvalidTokenOffsetsException {
        // 设置高亮标签,可以自定义,这里我用html将其显示为红色
        SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
        // 评分
        QueryScorer scorer = new QueryScorer(query);
        // 创建Fragmenter
        Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);
        // 高亮分析器
        Highlighter highlight = new Highlighter(formatter, scorer);
        highlight.setTextFragmenter(fragmenter);

        Analyzer analyzer = new StandardAnalyzer();

        // 调用高亮方法
        String str = highlight.getBestFragment(analyzer, fieldName, fieldContent);
        return str;
    }


    public static void createIndex(List<Map<String, Object>> mapList) throws IOException {


        // 2. 创建文档对象
        List<Document> documents = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            Document document = new Document();
            // 给文档对象添加域
            // add方法: 把域添加到文档对象中, field参数: 要添加的域
            // TextField: 文本域, 属性name:域的名称, value:域的值, store:指定是否将域值保存到文档中
            document.add(new TextField("title", map.get("title") + "", Field.Store.YES));
            document.add(new TextField("tag", map.get("tag")+"",  Field.Store.YES));

            // 将文档对象添加到文档对象集合中
            documents.add(document);
        }
        // 3. 创建分析器对象(Analyzer), 用于分词
        Analyzer analyzer = new StandardAnalyzer();
        // 4. 创建索引配置对象(IndexWriterConfig), 用于配置Lucene
        // 参数一:当前使用的Lucene版本, 参数二:分析器
        IndexWriterConfig indexConfig = new IndexWriterConfig( analyzer);
        // 5. 创建索引库目录位置对象(Directory), 指定索引库的存储位置
        File path = new File("C:\\Users\\xiong\\Desktop\\index");
        Directory directory = FSDirectory.open(Paths.get("C:\\Users\\xiong\\Desktop\\index"));
        // 6. 创建索引写入对象(IndexWriter), 将文档对象写入索引
        IndexWriter indexWriter = new IndexWriter(directory, indexConfig);
        // 7. 使用IndexWriter对象创建索引
        for (Document doc : documents) {
            // addDocement(doc): 将文档对象写入索引库
            indexWriter.addDocument(doc);
        }
        // 8. 释放资源
        indexWriter.close();
    }



    public static Document createDocument(Map<String, Object> stringObjectMap) {
        Document doc = new Document();
        for (Map.Entry<String, Object> objectEntry:stringObjectMap.entrySet()){
            doc.add(new StringField(objectEntry.getKey(), JSON.toJSONString(objectEntry.getValue()) , Field.Store.YES));
        }
        return doc;
    }


}
